<?php
/**
 * @author Green Leaf <786407418@qq.com>
 */

use App\Enums\BaseErrorCode;
use App\Enums\OpenShop\InvestmentPromotion\UserExperienceEnum;
use App\Error\BaseError;
use App\Exceptions\Custom\CustomException;
use App\Exceptions\Custom\CustomHttpResponseException;
use App\Exceptions\Custom\ResponseHttpException;
use App\Exceptions\KfHttpResponseException;
use App\Http\Middleware\OpenShop\FeishuWebAuth;
use App\Http\Middleware\OpenShop\MerchantAppAuth;
use App\Library\FeiShu\SendFs;
use App\Models\Article\ArticleModel;
use App\Service\OpenShop\User\RmsUserService;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Uri;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Kuafu\File\FileUtil;
use Kuafu\File\PrivateFileUtil;
use Kuafu\GeneralFs\BuildFs;
use Maatwebsite\Excel\Excel;
use Maatwebsite\Excel\Exceptions\NoTypeDetectedException;
use Maatwebsite\Excel\Files\Filesystem;
use Maatwebsite\Excel\Helpers\FileTypeDetector;
use Maatwebsite\Excel\QueuedWriter;
use Maatwebsite\Excel\Reader;
use Maatwebsite\Excel\Writer;
use OSS\Core\OssException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Http\Message\UriInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\HeaderUtils;
use Symfony\Component\HttpFoundation\Response;

if (!function_exists('picturePath')) {
    /**
     * @param string $path
     * @return string
     */
    function picturePath(string $path = ''): string
    {
        return empty($path) ? "" : (preg_match("/^http(s)?:\\/\\/.+/", $path,
            $matches) ? $path : FileUtil::domain() . '/' . ltrim($path,
                '/'));
    }
}

if (!function_exists('privateFilePath')) {
    /**
     * @param string $path
     * @return string
     */
    function privateFilePath(string $path = '', int $time = 86400): string
    {
        try {
            if ($time < 60) {
                $time = 86400;
            }
            return empty($path) ? "" : PrivateFileUtil::signUrl($path, $time);
        } catch (Exception $exception) {
            return picturePath($path);
        }
    }
}

if (!function_exists('enAes128Ecb')) {
    /**
     * @param string $data
     * @param string $key
     * @return string
     * 特殊处理 删掉最后面的两个等号
     */
    function enAes128Ecb(string $data, string $key): string
    {
        return substr(openssl_encrypt($data, 'AES-128-ECB', $key), 0, -2);
    }
}

if (!function_exists('deAes128Ecb')) {
    /**
     * @param string $data
     * @param string $key
     * @return string
     */
    function deAes128Ecb(string $data, string $key): string
    {
        return openssl_decrypt($data . '==', 'AES-128-ECB', $key);
    }
}

if (!function_exists('formatDate')) {
    /**
     * @param $time
     * @param string $default
     * @return string
     */
    function formatDate($time, string $default = '-'): string
    {
        return $time > 0 ? date("Y-m-d H:i:s", $time) : $default;
    }
}

if (!function_exists('formatDateWithRule')) {
    /**
     * @param $time
     * @param string $rule
     * @param string $default
     * @return string
     */
    function formatDateWithRule($time, string $rule, string $default = '-'): string
    {
        return $time > 0 ? date($rule, $time) : $default;
    }
}

if (!function_exists('formatDateYmd')) {
    /**
     * @param $time
     * @param string $default
     * @return string
     */
    function formatDateYmd($time, string $default = ""): string
    {
        return $time > 0 ? date("Y-m-d", $time) : $default;
    }
}

if (!function_exists('logException')) {
    /**
     * @param string $title
     * @param Exception $exception
     * @param array|null $logParams
     * @param bool $logError
     * @param bool $fsNotify
     * @param string $fsRobot
     * @throws Exception
     */
    function logException(
        string $title,
        \Exception $exception,
        array $logParams = null,
        bool $logError = true,
        bool $fsNotify = false,
        string $fsRobot = SendFs::ROBOT_WARNING
    ): void {
        if ($fsNotify || $logError) {
            $refException = new \ReflectionClass($exception);
            $properties   = $refException->getProperties(\ReflectionProperty::IS_PROTECTED);
            $return       = [];
            foreach ($properties as $property) {
                // 为了兼容反射私有属性
                $property->setAccessible(true);
                // 将得到的类属性同具体的实例绑定解析，获得实例上的属性值
                $return[$property->getName()] = $property->getValue($exception);
            }

            $msgCollection = new Collection(array_merge($return, ["logParams" => $logParams ?: []], [
                'time'  => date('Y-m-d H:i:s', time()),
                'trace' => $exception->getTraceAsString()
            ]));

            if ($fsNotify) {
                BuildFs::newRobot($fsRobot)
                       ->setTitleContent($title, $msgCollection->toJson(JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT))
                       ->send();
            }

            if ($logError) {
                Log::error($title, $msgCollection->toArray());
            }
        }
    }
}

if (!function_exists('formatConfigEnum')) {
    /**
     * 枚举配置转客户端直接展示
     *
     * @param array $config
     * @return array
     */
    function formatConfigEnum(array $config): array
    {
        return collect($config)->map(function ($v, $k) {
            return [
                "id"   => (string)$k,
                "name" => (string)$v
            ];
        })->values()->toArray();
    }
}

if (!function_exists('getRmsUserInfoByAuth')) {
    /**
     * @return object
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function getRmsUserInfoByAuth(): ?object
    {
        return app()->get('request')->userInfo;
    }
}


if (!function_exists('getRmsUserId')) {
    /**
     *
     * 获取 RMS 对应用户 ID
     *
     * @return int
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function getRmsUserId(): ?int
    {
        return getRmsUserInfoByAuth()->id ?? 0;
    }
}

if (!function_exists('getRmsUserPermissionList')) {
    /**
     * @param array $permissions
     * @param string $module
     * @return array
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function getRmsUserPermissionList(
        array $permissions = [],
        string $module = \App\Http\Middleware\OpenShop\UserPermissionAuth::MODULE_OPEN_SHOP
    ): array {
        if (!$permissions) {
            $permissions = getRmsUserInfoByAuth()->permissions;
        }

        $module = strtolower($module);

        // 获取开店的权限列表
        $permissionList = [];
        foreach ($permissions as $p) {
            $perStr = explode('.', $p);

            if (count($perStr) > 1 && $perStr[1] == $module) {
                $permissionList[] = ltrim($p, 'b.');
            }
        }

        return $permissionList;
    }
}

if (!function_exists('getControllerUri')) {

    /**
     * @return string
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function getControllerUri(): string
    {
        return app()->get('request')->controllerUri ?? '';
    }
}

if (!function_exists('formatConfigEnum')) {
    /**
     * 枚举配置转客户端直接展示
     *
     * @param array $config
     * @return array
     */
    function formatConfigEnum(array $config): array
    {
        return collect($config)->map(function ($v, $k) {
            return [
                "key"   => (string)$k,
                "value" => (string)$v
            ];
        })->toArray();
    }
}

if (!function_exists('getRmsMessageCommon')) {

    /**
     * @param string $method 请求方式
     * @param string $url 请求地址
     * @param  $body
     * @param array $headers 请求头
     * @param string $bodyType 请求参数类型  如 body json query
     * @return mixed
     * @throws GuzzleException
     */
    function getRmsMessageCommon(
        string $method,
        string $url,
        $body,
        array $headers = [],
        string $bodyType = 'body'
    ) {
        $client   = new GuzzleClient([
            'http_errors' => false,
        ]);
        $response = $client->request($method, $url, [
            $bodyType => $body,
            'headers' => $headers,
            'timeout' => 10
        ]);
        $contents = $response->getBody()->getContents();
        return json_decode($contents, true);
    }
}

if (!function_exists('getOrmMessageCommon')) {

    /**
     * @param string $method 请求方式
     * @param string $url 请求地址
     * @param  $body
     * @param array $headers 请求头
     * @param string $bodyType 请求参数类型  如 body json query
     * @return mixed
     * @throws GuzzleException
     */
    function getOrmMessageCommon(
        string $method,
        string $url,
        $body,
        array $headers = [],
        string $bodyType = 'body'
    ) {
        $client   = new GuzzleClient([
            'http_errors' => false,
        ]);
        $response = $client->request($method, $url, [
            $bodyType => $body,
            'headers' => $headers,
            'timeout' => 10
        ]);
        $contents = $response->getBody()->getContents();
        return json_decode($contents, true);
    }
}

if (!function_exists('getRmsReqHeader')) {
    function getRmsReqHeader(array $headers = [], $shopId = null): array
    {
        return [
            'brandId'        => (int)request()->header('brandId', 1),
            'shopId'         => $shopId === null ? (int)request()->header('shopId', 0) : $shopId,
            'internal-key'   => config('rms.internalKey'),
            'internal-token' => config('rms.internalValue'),
            'authorization'  => $headers['authorization'][0] ?? '',
        ];
    }
}

if (!function_exists('enumTransToRules')) {
    /**
     * @param array $enum
     * @param array $extraArr
     * @return string
     */
    function enumTransToRules(array $enum, array $extraArr = []): string
    {
        return implode(",", array_keys($enum)) . (empty($extraArr) ? '' : ',' . implode(",", $extraArr));
    }
}

if (!function_exists('generateOrderNo')) {
    /**
     * @param string $prefix
     * @param null $machineId
     * @return string
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function generateOrderNo(string $prefix = '', $machineId = null): string
    {
        $epoch    = 1479533469598;
        $max12bit = 4095;
        $max41bit = 1099511627775;
        $time     = floor(microtime(true) * 1000);
        $time     = $time - $epoch;
        $base     = decbin($max41bit + $time);
        if ($machineId && $machineId < 1024 && $machineId > 0) {
            $machineId = str_pad(decbin($machineId), 10, "0", STR_PAD_LEFT);
        } else {
            $machineId = str_pad(decbin(array_sum(explode('.', app()->get('request')->ip()))), 10, "0", STR_PAD_LEFT);
        }
        $random = str_pad(decbin(mt_rand(0, $max12bit)), 12, "0", STR_PAD_LEFT);
        $base   = $base . $machineId . $random;
        return $prefix . bindec($base);
    }
}

if (!function_exists("formatPrice")) {
    /**
     * @param $price
     * @return string|null
     */
    function formatPrice($price): ?string
    {
        return bcdiv($price, 100, 2);
    }
}

if (!function_exists("convertAmountToCn")) {
    /**
     * 将数值金额转换为中文大写金额
     * @param $num float 金额(支持到分)
     * @return string 中文大写金额
     */
    function convertAmountToCn(float $num): string
    {
        $digits           = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
        $radices          = ['', '拾', '佰', '仟', '万', '亿'];
        $bigRadices       = ['', '万', '亿'];
        $decimals         = ['角', '分'];
        $cn_dollar        = '元';
        $cn_integer       = '整';
        $num_arr          = explode('.', $num);
        $int_str          = $num_arr[0] ?? '';
        $float_str        = $num_arr[1] ?? '';
        $outputCharacters = '';
        if ($int_str) {
            $int_len   = strlen($int_str);
            $zeroCount = 0;
            for ($i = 0; $i < $int_len; $i++) {
                $p        = $int_len - $i - 1;
                $d        = substr($int_str, $i, 1);
                $quotient = $p / 4;
                $modulus  = $p % 4;
                if ($d == "0") {
                    $zeroCount++;
                } else {
                    if ($zeroCount > 0) {
                        $outputCharacters .= $digits[0];
                    }
                    $zeroCount        = 0;
                    $outputCharacters .= $digits[$d] . $radices[$modulus];
                }
                if ($modulus == 0 && $zeroCount < 4) {
                    $outputCharacters .= $bigRadices[$quotient];
                    $zeroCount        = 0;
                }
            }
            $outputCharacters .= $cn_dollar;
        }
        if ($float_str) {
            $float_len = strlen($float_str);
            for ($i = 0; $i < $float_len; $i++) {
                $d = substr($float_str, $i, 1);
                if ($d != "0") {
                    $outputCharacters .= $digits[$d] . $decimals[$i];
                }
            }
        }
        if ($outputCharacters == "") {
            $outputCharacters = $digits[0] . $cn_dollar;
        }
        if ($float_str) {
            $outputCharacters .= $cn_integer;
        }
        return $outputCharacters;
    }
}


if (!function_exists("convertPregMatchToArr")) {
    /**
     * @param array $matches
     * @return array
     */
    function convertPregMatchToArr(array $matches): array
    {
        $doc = [];
        if (!empty($matches) && count($matches) == 4 && !empty($matches[2])) {
            foreach ($matches[2] as $key => $val) {
                $doc[$val] = $matches[3][$key];
            }
        }
        return $doc;
    }
}

if (!function_exists("handleException")) {
    /**
     * 处理异常并返回客户端
     *
     * @param Exception $exception
     * @param int|null $errorCode
     * @param array|null $logData
     * @param string $msg
     * @param string $fsRobot
     * @throws ValidationException
     */
    function handleException(
        Exception $exception,
        int $errorCode = null,
        array $logData = null,
        string $msg = "",
        string $fsRobot = SendFs::ROBOT_WARNING
    ) {
        if ($exception instanceof ResponseHttpException || $exception instanceof CustomHttpResponseException || $exception instanceof ValidationException) {
            throw $exception;
        } else {
            if (config("app.env") == "qa") {
                throw $exception;
            } else {
                throw new ResponseHttpException(
                    $errorCode ?: BaseErrorCode::$ERROR_OCCURRED,
                    $exception,
                    $msg,
                    $logData,
                    true,
                    true,
                    Response::HTTP_OK,
                    $fsRobot
                );
            }
        }
    }
}


if (!function_exists("handleKfException")) {
    /**
     * 处理异常并返回客户端
     *
     * @param Exception $exception
     * @param array $error
     * @param array $logData
     * @param bool $logError
     * @param bool $fsNotify
     * @param string $fsRobot
     * @throws Exception
     */
    function handleKfException(
        Exception $exception,
        array $error = [],
        array $logData = [],
        bool $logError = true,
        bool $fsNotify = true,
        string $fsRobot = SendFs::ROBOT_WARNING
    ) {
        if ($exception instanceof KfHttpResponseException) {
            throw $exception;
        }
        logException(
            "程序运行报错",
            $exception,
            ["logData" => $logData, "responseError" => $error],
            $logError,
            $fsNotify,
            $fsRobot
        );
        throw new KfHttpResponseException(empty($error) ? BaseError::SERVER_INTERNAL_ERROR : $error);
    }
}


if (!function_exists("getRequest")) {
    /**
     * @return Request
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function getRequest(): Request
    {
        return app()->get("request");
    }
}

if (!function_exists("enumToMigrationComment")) {
    /**
     * @param array $enum
     * @param string $title
     * @return string
     */
    function enumToMigrationComment(array $enum, string $title = ""): string
    {
        $comment = $title . " [ ";
        foreach ($enum as $key => $value) {
            $comment .= $key . ":" . $value . " ";
        }
        return $comment . "]";
    }
}

if (!function_exists("getSwitchCacheTime")) {
    /**
     * 根据不同环境获取缓存时间
     *
     * @param int $time
     * @return int
     */
    function getSwitchCacheTime(int $time): int
    {
        if (config('app.env') == 'prod' || config('app.env') == 'production') {
            return $time;
        }
        return 5;
    }
}


if (!function_exists("getPrivateFileKey")) {
    /**
     * 获取私有文件key
     *
     * @param string $fileFullUrl
     * @return mixed|string
     */
    function getPrivateFileKey(string $fileFullUrl)
    {
        try {
            return trim(parse_url($fileFullUrl)["path"], "/");
        } catch (Exception $exception) {
            throw new ResponseHttpException(BaseErrorCode::$ERROR_PRIVATE_FILE_PATH);
        }
    }
}

if (!function_exists("generateFileKey")) {
    /**
     * 生成上传文件key
     *
     * @param string $path
     * @return string
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function generateFileKey(string $path = "openShop"): string
    {
        return "{$path}/" . md5(vsprintf("%s-%s-%s-%s", [
                getRmsUserInfoByAuth()->id ?? 0,
                getRequest()->ip(),
                microtime(true) * 1000,
                random_int(1000, 9999)
            ]));
    }
}

if (!function_exists('bcdiv100')) {

    function bcdiv100($num): string
    {
        return bcdiv($num, 100, 2);
    }
}

if (!function_exists('translateEnumCodeToCn')) {
    /**
     * @param array $enum
     * @param int|string $code
     * @param string $default
     * @return string
     */
    function translateEnumCodeToCn(array $enum, $code, string $default = "-"): string
    {
        return $enum[$code] ?? $default;
    }
}

if (!function_exists('arraySort')) {
    /**
     * 二维数组根据某个字段排序
     * @param array $array 要排序的数组
     * @param string $keys 要排序的键字段
     * @param string $sort 排序类型  SORT_ASC     SORT_DESC
     * @return array 排序后的数组
     */
    function arraySort(array $array, string $keys, $sort = SORT_DESC): array
    {
        $keysValue = [];
        foreach ($array as $k => $v) {
            $keysValue[$k] = $v[$keys];
        }
        array_multisort($keysValue, $sort, $array);
        return $array;
    }
}

if (!function_exists('checkEnv')) {
    /**
     * @param string $env
     * @return bool
     */
    function checkEnv(string $env): bool
    {
        if (config('app.env') == $env) {
            return true;
        } else {
            return false;
        }
    }
}

if (!function_exists("logInfo")) {

    function logInfo(
        string $logTitle,
        Collection $logContent,
        bool $logFile = true,
        bool $logFs = false,
        string $fsRobot = SendFs::ROBOT_WARNING
    ) {
        if ($logFs) {
            BuildFs::newRobot($fsRobot)
                   ->setTitleContent($logTitle, $logContent->toJson(JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT))
                   ->send();
        }

        if ($logFile) {
            Log::error($logTitle, $logContent->toArray());
        }
    }
}

if (!function_exists("logBusinessError")) {
    function logBusinessError(
        string $logTitle,
        string $logContent,
        string $fsRobot = SendFs::ROBOT_WARNING,
        array $atUserIds = []
    ) {
        $fs = BuildFs::newRobot($fsRobot);

        $fs->setTitleContent($logTitle, $logContent);
        if ($atUserIds) {
            foreach ($atUserIds as $userId) {
                $fs->setAtUid($userId);
            }
        }
        $fs->send();

        Log::error($logTitle, ["content" => $logContent]);
    }
}


if (!function_exists("formatExportFileName")) {
    /**
     * @param string $fileName
     * @param string $suffix
     * @return string
     */
    function formatExportFileName(string $fileName, string $suffix = "xlsx"): string
    {
        return vsprintf("%s-%s.%s", [urlencode($fileName), date("Y-m-d-H-i", time()), $suffix]);
    }

}


if (!function_exists("validateDateTimeFormat")) {
    /**
     * @param string $value
     * @param string $format
     * @return bool
     */
    function validateDateTimeFormat(string $value, string $format): bool
    {
        $date = DateTime::createFromFormat('!' . $format, $value);
        if ($date && $date->format($format) == $value) {
            return true;
        }
        return false;
    }

}


if (!function_exists("excelDownload")) {

    /**
     * @param $export
     * @param string $fileName
     * @param string $suffix
     * @param string|null $writerType
     * @param array $headers
     * @param string $disposition
     * @return array
     * @throws BindingResolutionException
     * @throws NoTypeDetectedException
     * @throws OssException
     */
    function excelDownload(
        $export,
        string $fileName,
        string $suffix = "csv",
        string $writerType = null,
        array $headers = [],
        string $disposition = "attachment"
    ): array {

        $exportFilename = formatExportFileName($fileName, $suffix);
        $writerType     = FileTypeDetector::detectStrict($exportFilename, $writerType);
        $fileLocalPath  = (app()->make(Writer::class))->export($export, $writerType)->getLocalPath();

        $OssObjectName = "tmp/" . date('Ymd') . "/" . Str::random() . "/" . urlencode($fileName) . ".{$suffix}";
        return FileUtil::uploadFile(
            $fileLocalPath,
            $suffix,
            $OssObjectName
        );
    }

}

if (!function_exists('isTest')) {

    function isTest(): bool
    {
        if (config('app.env') == 'dev' || config('app.env') == 'qa') {
            return true;
        }

        return false;
    }
}

if (!function_exists('isProd')) {

    function isProd(): bool
    {
        if (config('app.env') == 'prod') {
            return true;
        }

        return false;
    }
}

if (!function_exists('isPre')) {

    function isPre(): bool
    {
        if (config('app.env') == 'pre') {
            return true;
        }

        return false;
    }
}


if (!function_exists("getFileSystem")) {
    /**
     * @return \Illuminate\Filesystem\Filesystem
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function getFileSystem(): \Illuminate\Filesystem\Filesystem
    {
        return app()->get("files");
    }
}


if (!function_exists("downloadFile")) {
    /**
     * @param string $url
     * @param string $path
     * @param string $name
     * @param string $suffix
     * @return string
     * @throws ContainerExceptionInterface
     * @throws GuzzleException
     * @throws NotFoundExceptionInterface
     */
    function downloadFile(string $url, string $path, string $name, string $suffix): string
    {
        $fileSystem = getFileSystem();
        $dir        = app()->storagePath() . "/" . $path;
        if (!$fileSystem->isDirectory($dir)) {
            $fileSystem->makeDirectory($dir, 0777, true);
        }

        $fileName = $dir . "/{$name}.{$suffix}";
        $client   = new GuzzleClient();
        $response = $client->get($url);
        file_put_contents($fileName, $response->getBody());

        return $fileName;
    }
}

if (!function_exists("logFile")) {
    /**
     * @param string $content
     * @param string $path
     * @param string $name
     * @param string $suffix
     * @return string
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function logFile(string $content, string $path, string $name, string $suffix): string
    {
        $fileSystem = getFileSystem();
        $dir        = app()->storagePath() . "/" . $path;
        if (!$fileSystem->isDirectory($dir)) {
            $fileSystem->makeDirectory($dir, 0777, true);
        }

        $fileName = $dir . "/{$name}.{$suffix}";
        file_put_contents($fileName, $content);

        return $fileName;
    }
}


if (!function_exists("getTmpDir")) {
    /**
     * @param string $dir
     * @return string
     */
    function getTmpDir(string $dir): string
    {
        if (in_array(config("app.env"), ["local"])) {
            $basePath = storage_path();
        } else {
            $basePath = sys_get_temp_dir();
        }
        return vsprintf("%s/%s", [$basePath, trim($dir, "/")]);
    }
}


/**
 * 枚举转select
 */
if (!function_exists('enumToSelection')) {
    /**
     * @param array $enum
     * @return array
     */
    function enumToSelection(array $enum): array
    {
        return collect($enum)->map(function ($v, $k) {
            return [
                "key"   => $v,
                "value" => $k
            ];
        })->values()->toArray();
    }
}


if (!function_exists("buildParsedUrl")) {
    /**
     * @param array|UriInterface $parsedUrl
     * @return string
     */
    function buildParsedUrl($parsedUrl): string
    {
        if ($parsedUrl instanceof Uri) {
            $parsedUrl = [
                "scheme"   => $parsedUrl->getScheme(),
                "host"     => $parsedUrl->getHost(),
                "port"     => $parsedUrl->getPort(),
                "path"     => $parsedUrl->getPath(),
                "query"    => $parsedUrl->getQuery(),
                "fragment" => $parsedUrl->getFragment(),
            ];
        }

        $url = '';

        if (!empty($parsedUrl['host'])) {
            if (!empty($parsedUrl['scheme'])) {
                $url .= $parsedUrl['scheme'] . ':';
            }

            $url .= '//' . trim($parsedUrl['host'], '/');
            if (!empty($parsedUrl['port'])) {
                $url .= ':' . $parsedUrl['port'];
            }
        }

        $url .= '/' . ltrim($parsedUrl['path'], '/');

        if (!empty($parsedUrl['query'])) {
            $url .= '?' . $parsedUrl['query'];
        }

        if (!empty($parsedUrl['fragment'])) {
            $url .= '#' . $parsedUrl['fragment'];
        }

        return $url;
    }
}

if (!function_exists("remoteContentsParser")) {
    /**
     * @param string $responseStr
     * @param null $default
     * @return mixed|null
     */
    function remoteContentsParser(string $responseStr, $default = null)
    {
        $xmlParser = xml_parser_create();
        if (xml_parse($xmlParser, $responseStr, true)) {
            $jsonStr = json_encode(simplexml_load_string($responseStr));
            return json_decode(str_replace(["\\n", PHP_EOL], "", $jsonStr), true);
        }

        return isJson($responseStr) ? json_decode($responseStr, true) : $default;
    }
}

if (!function_exists("isJson")) {
    /**
     * @param String|null $jsonStr
     * @return bool
     */
    function isJson( ?string $jsonStr): bool
    {
        json_decode($jsonStr);
        if (json_last_error()) {
            return false;
        }
        return true;
    }
}

if (!function_exists("jsonToArr")) {
    /**
     * @param string|null $jsonStr
     * @param null $default
     * @return mixed|null
     */
    function jsonToArr( ?string $jsonStr, $default = null)
    {
        return isJson($jsonStr) ? json_decode($jsonStr, true) : $default;
    }
}

if (!function_exists("underscoreToCamelCase")) {
    /**
     * @param String $jsonStr
     * @param null $default
     * @return mixed|null
     */
    function underscoreToCamelCase($string, $capitalizeFirstCharacter = false)
    {
        $str = str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
        if (!$capitalizeFirstCharacter) {
            $str = lcfirst($str);
        }
        return $str;
    }
}

if (!function_exists("getRedisClient")) {
    /**
     * @param string|null $connection
     * @return mixed|Redis
     */
    function getRedisClient(string $connection = null)
    {
        return \Illuminate\Support\Facades\Redis::connection($connection)->client();
    }
}

if (!function_exists("attributeGet")) {
    /**
     * 多级属性值获取 ｜ 支持数组/对象/collection
     *
     * @param $original
     * @param $attribute
     * @param $default
     * @return mixed
     */
    function attributeGet($original, $attribute, $default)
    {
        if (is_null($attribute) || trim($attribute) === '') {
            return $original;
        }

        foreach (explode('.', $attribute) as $segment) {
            switch (strtolower(gettype($original))) {
                case "array":
                    if (!isset($original[$segment])) {
                        return value($default);
                    }
                    $original = $original[$segment];
                    break;
                case "object":
                    if ($original instanceof Collection) {
                        if (!$original->offsetExists($segment)) {
                            return value($default);
                        }
                        $original = $original->offsetGet($segment);
                    } else {
                        if (!is_object($original) || !isset($original->{$segment})) {
                            return value($default);
                        }
                        $original = $original->{$segment};
                    }
                    break;
                default:
                    return $default;
                    break;
            }
        }

        return $original;
    }
}

if (!function_exists('execTime')) {
    function execTime(\Closure $closure, string $name = 'flag')
    {
        $start    = microtime(true);
        $res      = $closure();
        $duration = microtime(true) - $start;

        dump("{$name}: {$duration}s");

        return $res;
    }
}

if (!function_exists("calcDistance")) {
    /**
     * 计算两点地理坐标之间的距离
     *
     * @param float $longitude1 起点经度
     * @param float $latitude1 起点纬度
     * @param float $longitude2 终点经度
     * @param float $latitude2 终点纬度
     * @param int $unit 单位 1:米 2:公里
     * @param int $decimal 精度 保留小数位数
     * @return float
     */
    function calcDistance(
        float $longitude1,
        float $latitude1,
        float $longitude2,
        float $latitude2,
        int $unit = 1,
        int $decimal = 2
    ): float {
        $EARTH_RADIUS = 6370.996; // 地球半径系数
        $PI           = 3.1415926;

        $radLat1 = $latitude1 * $PI / 180.0;
        $radLat2 = $latitude2 * $PI / 180.0;

        $radLng1 = $longitude1 * $PI / 180.0;
        $radLng2 = $longitude2 * $PI / 180.0;

        $a = $radLat1 - $radLat2;
        $b = $radLng1 - $radLng2;

        $distance = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)));
        $distance = $distance * $EARTH_RADIUS * 1000;

        if ($unit == 2) {
            $distance = $distance / 1000;
        }

        return round($distance, $decimal);
    }
}


if (!function_exists('checkFieldLen')) {
    /**
     * @param object $params
     * @param string $field
     * @return int
     */
    function checkFieldLen(object $params, string $field): int
    {
        return strlen(attributeGet($params, $field, ""));
    }
}

/**
 * 判断请求来源
 */
if (!function_exists("requestFromFunc")) {
    /**
     * @return string
     */
    function requestFromFunc(): string
    {
        $action     = request()->route()->getAction();
        $middleware = attributeGet($action, "middleware", []);
        if (in_array(MerchantAppAuth::class, $middleware)) {
            return MerchantAppAuth::class;
        }
        if (in_array(FeishuWebAuth::class, $middleware)) {
            return FeishuWebAuth::class;
        }
        return "";
    }
}

/*
 * 获得毫秒时间
 */
if (!function_exists("getMillisecondsTimestamp")) {
    function getMillisecondsTimestamp(): int
    {
        $time = microtime(true); // 获取当前时间的秒数和微秒数
        // 将微秒数转换为毫秒数
        return (int)round($time * 1000);
    }
}
